package com.example.plantuml.docx;

import com.example.plantuml.PlantUmlClient;
import com.example.plantuml.ProgressListener;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Walks through paragraphs and runs, detects @startuml ... @enduml blocks
 * and replaces them with generated PNG images from PlantUML server.
 */
public final class DocxPlantUmlConverter {
    private final PlantUmlClient client;
    private ProgressListener listener;

    public DocxPlantUmlConverter(PlantUmlClient client) {
        this.client = client;
    }

    public void setProgressListener(ProgressListener listener) { this.listener = listener; }

    public void convert(File inputDocx, File outputDocx) throws IOException {
        try (FileInputStream fis = new FileInputStream(inputDocx);
             XWPFDocument doc = new XWPFDocument(fis)) {
            processDocument(doc);
            try (FileOutputStream fos = new FileOutputStream(outputDocx)) {
                doc.write(fos);
            }
        }
    }

    private void processDocument(XWPFDocument doc) throws IOException {
        List<Integer> bodyElementIndexes = new ArrayList<>();
        for (int i = 0; i < doc.getBodyElements().size(); i++) {
            bodyElementIndexes.add(i);
        }
        for (int idx : bodyElementIndexes) {
            IBodyElement el = doc.getBodyElements().get(idx);
            if (el instanceof XWPFParagraph) {
                replaceInParagraph((XWPFParagraph) el);
            } else if (el instanceof XWPFTable) {
                XWPFTable table = (XWPFTable) el;
                for (XWPFTableRow row : table.getRows()) {
                    for (XWPFTableCell cell : row.getTableCells()) {
                        for (IBodyElement cellEl : cell.getBodyElements()) {
                            if (cellEl instanceof XWPFParagraph) {
                                replaceInParagraph((XWPFParagraph) cellEl);
                            }
                        }
                    }
                }
            }
        }
    }

    private void replaceInParagraph(XWPFParagraph paragraph) throws IOException {
        StringBuilder paraText = new StringBuilder();
        for (XWPFRun run : paragraph.getRuns()) {
            paraText.append(run.toString());
            paraText.append("\n");
        }
        String text = paraText.toString();
        int start = text.indexOf("@startuml");
        int end = text.indexOf("@enduml");
        if (start >= 0 && end > start) {
            String block = text.substring(start, end + "@enduml".length());
            if (listener != null) listener.onProgress("Rendering PlantUML diagram...");
            byte[] png;
            try {
                png = client.renderPng(block);
            } catch (Exception e) {
                if (listener != null) listener.onProgress("Diagram failed: " + e.getMessage() + " (skipped)");
                return; // skip this diagram and continue with the rest
            }
            // clear existing runs in this paragraph
            int runCount = paragraph.getRuns().size();
            for (int i = runCount - 1; i >= 0; i--) {
                paragraph.removeRun(i);
            }
            XWPFRun imageRun = paragraph.createRun();
            // compute size by reading PNG dimensions and set width to 8.8 cm, keeping aspect ratio
            try (InputStream dimIn = new ByteArrayInputStream(png)) {
                BufferedImage img = ImageIO.read(dimIn);
                if (img == null) throw new IOException("Server did not return a PNG image");
                int imgW = img.getWidth();
                int imgH = img.getHeight();
                // fixed width: 8.8 cm; compute EMU precisely to avoid Units.toEMU ambiguity
                final double inches = 8.8 / 2.54;
                final int EMU_PER_INCH = 914400;
                int targetWidthEmu = (int) Math.round(inches * EMU_PER_INCH);
                int targetHeightEmu = (int) ((long) targetWidthEmu * imgH / imgW);

                try (InputStream is = new ByteArrayInputStream(png)) {
                    imageRun.addPicture(is, Document.PICTURE_TYPE_PNG, "diagram.png", targetWidthEmu, targetHeightEmu);
                }
                paragraph.setAlignment(ParagraphAlignment.CENTER);
            } catch (Exception e) {
                if (listener != null) listener.onProgress("Failed to insert picture: " + e.getMessage() + " (skipped)");
                return; // skip this diagram
            }
            if (listener != null) listener.onProgress("Diagram inserted.");
            try { Thread.sleep(1000); } catch (InterruptedException ignored) { }
        }
    }

    private static int parseIntSafely(Object value, int defaultValue) {
        if (value == null) return defaultValue;
        if (value instanceof Number) return ((Number) value).intValue();
        try {
            return new java.math.BigInteger(value.toString()).intValue();
        } catch (Exception ignored) {
            try {
                return Integer.parseInt(value.toString());
            } catch (Exception ignored2) {
                return defaultValue;
            }
        }
    }
}


